﻿#define ASSET_CTL_SELF
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.IO;
using System.Security.Cryptography;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace DashFire
{
  public delegate void MyAction();
  public delegate void MyAction<T1>(T1 t1);
  internal class LoaderManager
  {
    static AssetBundle manifestBundle = null;
    static AssetBundleManifest manifest = null;
    static bool isInit = false;

    static string s_AssetBundleURLAbs = null;

    private static Dictionary<string, ResAsyncInfo> s_AsyncLoadAssetbundleList = new Dictionary<string, ResAsyncInfo>();

    public static Dictionary<string, AssetBundle> s_LoadedAssetbundle = new Dictionary<string, AssetBundle>();

    private static Dictionary<string, WeakReference> s_cacheResObjList = new Dictionary<string, WeakReference>();
    private static Dictionary<string, WeakReference> s_cacheAddResObjList = new Dictionary<string, WeakReference>();

#if ASSET_CTL_SELF
    private static HashSet<string> s_LoadedScenesList = new HashSet<string>();

    private class AssetBundleCacheConfig
    {
      public AssetBundle assetBundle;
      public int refNum;
    };
    private static Dictionary<string, AssetBundleCacheConfig> s_cacheAssetBundleCfgList = new Dictionary<string, AssetBundleCacheConfig>();
#endif

    private static List<string> s_tempCachelist = new List<string>();
    private static float s_LastTickTime = 0;
    private static float s_TickStep = 3;

    public static void InitContext()
    {
      ResUpdateHandler.HandleStartCoroutine = LoaderManager.StartCoroutineImpl;
      ResUpdateHandler.HandleIsResPackaged = LoaderManager.IsResAssetCached;
      ResUpdateHandler.HandleLoadLevelAsync = LoaderManager.LoadLevelAsync;
      ResUpdateHandler.HandleLoadResource = LoaderManager.LoadResByCachedAssetBundle;
      //ResUpdateHandler.HandleLoadResourceAsync = LoaderManager.LoadResByCachedAssetBundleAsync;
      ResUpdateHandler.HandlePreloadResByLevel = LoaderManager.PreloadResAssetByLevel;
      ResUpdateHandler.HandleTryGetLoadedPrefabs = LoaderManager.TryGetLoadedResourceByName;
      ResUpdateHandler.HandleAddLoadedResource = LoaderManager.AddLoadedResource;
#if ASSET_CTL_SELF
      ResUpdateHandler.HandleClean = LoaderManager.Clean;
#else
      ResUpdateHandler.HandleClean = LoaderManager.ForceClean;
#endif
    }

    public static UnityEngine.Object TryGetLoadedResourceByName(string resName)
    {
      UnityEngine.Object obj = null;
      WeakReference weakObj = null;
      if (s_cacheResObjList.TryGetValue(resName, out weakObj))
      {
        obj = weakObj.Target as UnityEngine.Object;
        return obj;
      }

      if (s_cacheAddResObjList.TryGetValue(resName, out weakObj))
      {
        obj = weakObj.Target as UnityEngine.Object;
        return obj;
      }

      return null;
    }

    public static void AddLoadedResource(string resName, WeakReference obj)
    {
      if (!s_cacheResObjList.ContainsKey(resName) && !s_cacheAddResObjList.ContainsKey(resName))
      {
        s_cacheAddResObjList.Add(resName, obj);
      }
      else
      {
        UnityEngine.Debug.LogErrorFormat("AddLoadedResource {0} err!", resName);
      }
    }

    public static ResAsyncInfo StartCoroutineImpl(IEnumerator routine)
    {
      ResAsyncInfo info = new ResAsyncInfo();
      info.CurCoroutine = CoroutineProxy.Instance.StartCoroutine(routine);
      return info;
    }

    public static bool IsResAssetCached(string resName)
    {
      string resCachedUrl = GetAssetBundleURLAbs() + GetAssetBundleNameByResName(resName);
      if (!File.Exists(resCachedUrl))
      {
        return false;
      }
      return true;
    }

    public static string GetPlatformName()
    {
#if UNITY_ANDROID
        return "Android";
#endif

#if UNITY_IPHONE
        return "IOS";
#endif

#if UNITY_STANDALONE_WIN
        return "Windows";
#endif

#if UNITY_STANDALONE_OSX
        return "OSX";
#endif
    }

    internal static void init()
    {
      if (!isInit)
      {
        manifestBundle = AssetBundle.LoadFromFile(GetAssetBundleURLAbs() + GetPlatformName());
      }

      if (manifestBundle != null)
      {
        AssetBundleManifest manifest = (AssetBundleManifest)manifestBundle.LoadAsset("AssetBundleManifest");
        if (manifest != null)
        {
          isInit = true;
        }
      }

      if (!isInit)
      {
        throw new Exception("Can not load manifestBundle!");
      }
    }

    public static void Tick()
    {

      float curTime = Time.realtimeSinceStartup;
      if (curTime - s_LastTickTime < s_TickStep)
      {
        return;
      }

      s_LastTickTime = curTime;


      s_tempCachelist.Clear();
      foreach (var cache in s_cacheAddResObjList)
      {
        if (cache.Value.Target == null)
        {
          s_tempCachelist.Add(cache.Key);
        }
      }
      for (int i = 0; i < s_tempCachelist.Count; i++)
      {
        s_cacheAddResObjList.Remove(s_tempCachelist[i]);
      }

      s_tempCachelist.Clear();
      foreach (var cache in s_cacheResObjList)
      {
        if (cache.Value.Target == null)
        {
          s_tempCachelist.Add(cache.Key);

          DepAssetBundleRefByResName(cache.Key);
        }
      }

      for (int i = 0; i < s_tempCachelist.Count; i++)
      {
        s_cacheResObjList.Remove(s_tempCachelist[i]);
      }
      if (s_tempCachelist.Count > 0)
      {
        Resources.UnloadUnusedAssets();
      }
      s_tempCachelist.Clear();

#if ASSET_CTL_SELF
      foreach (var cache in s_cacheAssetBundleCfgList)
      {
        if (cache.Value.refNum <= 0)
        {
          cache.Value.assetBundle.Unload(true);
          s_LoadedAssetbundle.Remove(cache.Key);
          s_tempCachelist.Add(cache.Key);
        }
      }

      for (int i = 0; i < s_tempCachelist.Count; i++)
      {
        s_cacheAssetBundleCfgList.Remove(s_tempCachelist[i]);
      }
      s_tempCachelist.Clear();
#endif
    }

    public static void Clean()
    {
      s_LastTickTime = 0;
      Tick();
    }

    public static void ForceClean()
    {
      //if (s_CurSceneAssetbundle != null)
      //{
      //  s_CurSceneAssetbundle.Unload(false);
      //  s_CurSceneAssetbundle = null;
      //}
      s_cacheResObjList.Clear();
      s_cacheAddResObjList.Clear();
#if ASSET_CTL_SELF
      s_cacheAssetBundleCfgList.Clear();
      s_LoadedScenesList.Clear();
#endif
      foreach (AssetBundle ab in s_LoadedAssetbundle.Values)
      {
        ab.Unload(true);
      }
      s_LoadedAssetbundle.Clear();

      Resources.UnloadUnusedAssets();
    }

#region LOAD_LEVEL

    internal class AsyncParam
    {
      public AssetBundle bundle;
      public LoadSceneMode loadMode;
    };

    public static ResAsyncInfo LoadLevelAsync(string levelName, LoadSceneMode loadMode = LoadSceneMode.Single)
    {

      ResUpdateCallback.OnStartLoad(levelName);

      MyAction<UnityEngine.Object> callback = (asset) => {
        Resources.UnloadUnusedAssets();
        ResUpdateCallback.OnEndLoad(levelName);
      };

      AsyncParam para = new AsyncParam();
      para.bundle = null;
      para.loadMode = loadMode;

      return LoadCachedAssetBundleAsync(levelName, para, callback, LoadLevelImplAsync);
    }

    public static void LoadLevel(string levelName)
    {
#if ASSET_CTL_SELF
      foreach (var level in s_LoadedScenesList)
      {
        DepAssetBundleRefByResName(level);
      }
      s_LoadedScenesList.Clear();
#endif
      DFProfiler.Start("LoadLevel");
      ResUpdateCallback.OnStartLoad(levelName);

      AssetBundle assetBundle = LoadCachedAssetBundle(levelName);
      //if (assetBundle != null)
      //{
        SceneManager.LoadScene(levelName);
#if ASSET_CTL_SELF
        s_LoadedScenesList.Add(levelName);
#endif
      //}
      //else
      //{
      //  throw new ArgumentNullException("assetBundle");
      //}

      if (null != assetBundle)
      {
        assetBundle.Unload(false);
      }

      DFProfiler.Stop("LoadLevel");
      Resources.UnloadUnusedAssets();
      ResUpdateCallback.OnEndLoad(levelName);
    }

    public static void UnLoadLevel(string levelName)
    {
#if ASSET_CTL_SELF
      if (s_LoadedScenesList.Contains(levelName))
      {
        SceneManager.UnloadSceneAsync(levelName);
        DepAssetBundleRefByResName(levelName);
        s_LoadedScenesList.Remove(levelName);
      }
      else
      {
        throw new ArgumentNullException("not load this level");
      }
#else
      SceneManager.UnloadSceneAsync(levelName);
#endif
    }

    public static ResAsyncInfo PreloadResAssetByLevel(int sceneId)
    {
      ResAsyncInfo info = new ResAsyncInfo();
      info.CurCoroutine = CoroutineProxy.Instance.StartCoroutine(PreloadResAssetByLevelAsync(info, sceneId));
      return info;
    }
    internal static IEnumerator PreloadResAssetByLevelAsync(ResAsyncInfo info, int sceneId)
    {
      Dictionary<int, StaticResCacheData> cacheDataDict = new Dictionary<int, StaticResCacheData>();
      StaticResCacheData levelCacheData = null;
      StaticResCacheDataProvider.Instance.SearchCacheDataByLevelId(sceneId, out levelCacheData, ref cacheDataDict);

      HashSet<string> assetSet = new HashSet<string>();
      if (cacheDataDict != null && cacheDataDict.Count > 0)
      {
        foreach (StaticResCacheData cacheData in cacheDataDict.Values)
        {
          assetSet.UnionWith(cacheData.m_AssetNames);
        }
      }

      List<string> assetList = new List<string>(assetSet);
      string assetName = string.Empty;
      if (assetList != null && assetList.Count > 0)
      {
        for (int idx = 0; idx < assetList.Count; idx++)
        {
          try
          {
            assetName = assetList[idx];
            DashFire.ResourceSystem.PreloadSharedResource(assetName);
            info.Progress = 1.0f * idx / assetList.Count;
          }
          catch (System.Exception ex)
          {
            ResLoadHelper.ErrLog(string.Format("PreloadResAssetByLevelAsync Exception:{0}/{1}", ex.Message, ex.StackTrace));
          }
          if (idx % ResUpdateConfig.s_PreloadFrameNumMax == 0)
          {
            yield return 1;
          }
        }
      }
      info.IsDone = true;
      info.Progress = 1.0f;
    }
#endregion

#region LOAD_ASSERBUNDLE
    public static AssetBundle LoadAssetBundle(string abUrl)
    {
      if (!isInit)
      {
        init();
      }

      AssetBundle assetBundle = null;
      try
      {
        ResAsyncInfo tryGetValue = null;
        if (s_AsyncLoadAssetbundleList.TryGetValue(abUrl, out tryGetValue))
        {
          while (true)
          {
            if (tryGetValue.IsError || tryGetValue.IsDone || tryGetValue.Progress == 1.0f)
            {
              break;
            }
            Thread.Sleep(1);
          }

          if (!tryGetValue.IsError)
          {
            AssetBundleCreateRequest operation = tryGetValue.Operation as AssetBundleCreateRequest;
            return operation.assetBundle;
          }
          else
          {
            UnityEngine.Debug.Log("LoadAssetBundle failed By wait AsyncLoad abUrl:" + abUrl);
            return null;
          }
        }

        //UnityEngine.Debug.Log("LoadAssetBundle ab abUrl:" + abUrl);
        assetBundle = AssetBundle.LoadFromFile(abUrl);
        if (assetBundle == null)
        {
          UnityEngine.Debug.Log("LoadAssetBundle null url:" + abUrl);
          ResUpdateControler.s_UpdateError = ResUpdateError.Create_Assetbundle_Error;
          return null;
        }
      }
      catch (System.Exception ex)
      {
        UnityEngine.Debug.Log("LoadAssetBundle failed abUrl:" + abUrl + " ex:" + ex);
        return null;
      }
      return assetBundle;
    }

    // Resources/.../...  or scene's name
    private static string GetAssetBundleNameByResName(string resName)
    {
      if (string.IsNullOrEmpty(resName))
        return null;

      resName.Replace("\\", "/");
      int index = resName.IndexOf("/");
      if(index <= 0 || index >= resName.Length)
        return resName;

      return resName.Substring(0, index).ToLower();
    }

    public static string GetAssetBundleURLAbs()
    {
      if (!string.IsNullOrEmpty(s_AssetBundleURLAbs))
      {
        return s_AssetBundleURLAbs;
      }
      else
      {
        StringBuilder sb = new StringBuilder();

#if UNITY_EDITOR
        sb.Append(Application.streamingAssetsPath);
        sb.Append("/" + GetPlatformName() + "/");
#else
#if UNITY_ANDROID || UNITY_IOS
          sb.Append(Application.persistentDataPath);
          sb.Append("/ResFile/");
#else
        sb.Append(Application.streamingAssetsPath);
        sb.Append("/" + GetPlatformName() + "/");
#endif
#endif
        //sb.Append(ResUpdateConfig.s_ResCachePath);
        s_AssetBundleURLAbs = sb.ToString();
        return s_AssetBundleURLAbs;
      }
    }

    public static UnityEngine.Object LoadResByCachedAssetBundle(string resName)
    {
      UnityEngine.Object asset = GetResFromCache(resName);
      if (null != asset)
      {
        return asset;
      }
 
      AssetBundle assetBundle = LoadCachedAssetBundle(resName);
      if (assetBundle != null)
      {
        asset = assetBundle.LoadAsset("Assets/Resource/" + resName);
      }
      else
      {
        throw new ArgumentNullException("assetBundle");
      }

      s_cacheResObjList.Add(resName, new WeakReference(asset));

      return asset;
    }

    public static ResAsyncInfo LoadResByCachedAssetBundleAsync(string resName, MyAction<UnityEngine.Object> callback)
    {
      UnityEngine.Object asset = GetResFromCache(resName);
      if (null != asset)
      {
        if (null != callback)
          callback(asset);
        ResAsyncInfo info = new ResAsyncInfo(true);
        info.Result = asset;
        return info;
      }
      else
      {
        //DFProfiler.Start("LoadCachedAssetAsync" + resName);
        AsyncParam para = new AsyncParam();
        para.bundle = null;
        return LoadCachedAssetBundleAsync(resName, para, callback, LoadAssetImplAsync);
      }
    }
#endregion
#region TOOLS

    internal delegate IEnumerator LoadAssetBundleAction(ResAsyncInfo info, string resName, AsyncParam para, MyAction<UnityEngine.Object> callback);
    internal static ResAsyncInfo LoadCachedAssetBundleAsync(string resName, AsyncParam para, MyAction<UnityEngine.Object> callback, LoadAssetBundleAction loadcallback)
    {
      if (!isInit)
      {
        init();
      }

      AssetBundle assetBundle = null;
      //ClientResVersionData resVersion = null;
      try
      {
        string assetbundleName = GetAssetBundleNameByResName(resName);
        if (!string.IsNullOrEmpty(assetbundleName))
        {
          if (s_LoadedAssetbundle.TryGetValue(assetbundleName, out assetBundle))
          {
            if (assetBundle != null)
            {
#if ASSET_CTL_SELF
              AssetBundleCacheConfig abCfg = null;
              if (s_cacheAssetBundleCfgList.TryGetValue(assetBundle.name, out abCfg))
              {
                abCfg.refNum++;
              }
              else
              {
                throw new ArgumentNullException("AssetBundleCacheConfig");
              }
#endif

              ResAsyncInfo info = new ResAsyncInfo();
              info.IsDone = false;
              para.bundle = assetBundle;
              info.CurCoroutine = CoroutineProxy.Instance.StartCoroutine(loadcallback(info, resName, para, (asset) =>
              {
                //DFProfiler.Stop("LoadCachedAssetAsync_LoadAsset" + resVersion.m_AssetName);
                //DFProfiler.Stop("LoadCachedAssetAsync" + resName);            
                if (callback != null)
                {
                  callback(asset);
                }
                info.Result = asset;
                info.IsDone = true;
              }));
              //DFProfiler.Stop("LoadCachedAssetAsync" + resName);
              return info;
            }
            else
            {
              throw new ArgumentNullException("assetBundle");
            }
          }
          else
          {
            List<string> needLoadUrllist = new List<string>();
            string[] dependency = manifest.GetAllDependencies(assetbundleName);
            if (dependency != null && dependency.Length > 0)
            {
              foreach (string curDependBundle in dependency)
              {
                if (!s_LoadedAssetbundle.TryGetValue(curDependBundle, out assetBundle))
                {
                  needLoadUrllist.Add(curDependBundle);
                }
                else
                {
#if ASSET_CTL_SELF

                  AssetBundleCacheConfig abCfg = null;
                  if (s_cacheAssetBundleCfgList.TryGetValue(assetBundle.name, out abCfg))
                  {
                    abCfg.refNum++;
                  }
                  else
                  {
                    throw new ArgumentNullException("AssetBundleCacheConfig");
                  }
#endif
                }
              }
            }

            needLoadUrllist.Add(assetbundleName);

            ResAsyncInfo info = ResAsyncInfo.EmptyAsyncInfo;
            int asyncLoadCount = 0;
            for (int index = 0; index < needLoadUrllist.Count; index++)
            {
              info = LoadAssetBundleAsync(GetAssetBundleURLAbs() + needLoadUrllist[index], (bundle) =>
              {
                asyncLoadCount++;
                if (bundle != null)
                {
                  s_LoadedAssetbundle.Add(needLoadUrllist[index], bundle as AssetBundle);
#if ASSET_CTL_SELF
                  AssetBundleCacheConfig abCfg = null;
                  if (s_cacheAssetBundleCfgList.TryGetValue((bundle as AssetBundle).name, out abCfg))
                  {
                    throw new ArgumentNullException("AssetBundleCacheConfig");
                  }
                  else
                  {
                    abCfg = new AssetBundleCacheConfig();
                    abCfg.assetBundle = (bundle as AssetBundle);
                    abCfg.refNum = 1;
                    s_cacheAssetBundleCfgList.Add((bundle as AssetBundle).name, abCfg);
                  }
#endif
                }
                else
                {
                  throw new ArgumentNullException("bundle");
                }
                if (asyncLoadCount == needLoadUrllist.Count /*&& index == (needLoadUrllist.Count - 1)*/)
                {
                  //DFProfiler.Start("LoadCachedAssetAsync_LoadAsset" + resVersion.m_AssetName);

                  para.bundle = assetBundle;
                  info.CurCoroutine = CoroutineProxy.Instance.StartCoroutine(loadcallback(info, resName, para,(asset) =>
                  {
                    //DFProfiler.Stop("LoadCachedAssetAsync_LoadAsset" + resVersion.m_AssetName);
                    //DFProfiler.Stop("LoadCachedAssetAsync" + resName);                   
                    if (callback != null)
                    {
                      callback(asset);
                    }
                    info.Result = asset;
                    info.IsDone = true;
                  }));
                }
                else
                {
                  info.IsDone = false;
                }
              });
            }
            return info;
          }
        }
        else
        {
          throw new ArgumentNullException("assetbundleName");
        }
      }
      catch (System.Exception ex)
      {
        ResLoadHelper.Log(string.Format("LoadLevelImplAsync Exception:{0}/{1}", ex.Message, ex.StackTrace));
        //DFProfiler.Stop("LoadCachedAssetAsync" + resName);
      }

      return ResAsyncInfo.EmptyAsyncInfo;
    }

    internal static AssetBundle LoadCachedAssetBundle(string resName)
    {
      if (!isInit)
      {
        init();
      }

      AssetBundle assetBundle = null;
      //ClientResVersionData resVersion = null;
      try
      {
        string assetbundleName = GetAssetBundleNameByResName(resName);
        if (!string.IsNullOrEmpty(assetbundleName))
        {
          if (!s_LoadedAssetbundle.TryGetValue(assetbundleName, out assetBundle))
          {
            string[] dependency = manifest.GetAllDependencies(assetbundleName);
            if (dependency != null && dependency.Length > 0)
            {
              foreach (string curDependBundle in dependency)
              {
                if (!s_LoadedAssetbundle.TryGetValue(curDependBundle, out assetBundle))
                {
                  assetBundle = LoadAssetBundle(GetAssetBundleURLAbs() + curDependBundle);
                  if (assetBundle != null)
                  {
                    s_LoadedAssetbundle.Add(curDependBundle, assetBundle);
                  }
                }
#if ASSET_CTL_SELF
                AssetBundleCacheConfig abCfg = null;
                if (s_cacheAssetBundleCfgList.TryGetValue(assetBundle.name, out abCfg))
                {
                  abCfg.refNum++;
                }
                else
                {
                  abCfg = new AssetBundleCacheConfig();
                  abCfg.assetBundle = assetBundle;
                  abCfg.refNum = 1;
                  s_cacheAssetBundleCfgList.Add(assetBundle.name, abCfg);
                }
#endif
              }
            }

            assetBundle = LoadAssetBundle(GetAssetBundleURLAbs() + assetbundleName);
            if (assetBundle != null)
            {
              s_LoadedAssetbundle.Add(assetbundleName, assetBundle);
            }
          }
        }
        else
        {
          throw new ArgumentNullException("assetbundleName");
        }

        if (assetBundle != null)
        {
#if ASSET_CTL_SELF
          AssetBundleCacheConfig abCfg = null;
          if (s_cacheAssetBundleCfgList.TryGetValue(assetBundle.name, out abCfg))
          {
            abCfg.refNum++;
          }
          else
          {
            abCfg = new AssetBundleCacheConfig();
            abCfg.assetBundle = assetBundle;
            abCfg.refNum = 1;
            s_cacheAssetBundleCfgList.Add(assetBundle.name, abCfg);
          }
#endif
          return assetBundle;
        }
        else
        {
          throw new ArgumentNullException("assetBundle");
        }
      }
      catch (System.Exception ex)
      {
        ResLoadHelper.Log(string.Format("LoadCachedAssetBundle Exception:{0}/{1}", ex.Message, ex.StackTrace));
        assetBundle = null;
      }
      return assetBundle;
    }

    internal static IEnumerator LoadAssetImplAsync(ResAsyncInfo info, string resName, AsyncParam para, MyAction<UnityEngine.Object> callback)
    {
      AssetBundleRequest operation = null;
      try
      {
        operation = para.bundle.LoadAssetAsync("Assets/Resource/" + resName);
      }
      catch (System.Exception ex)
      {
        callback(null);
        UnityEngine.Debug.LogErrorFormat("LoadAssetImplAsync failed bundle:" + para.bundle.name + " resName:" + resName + " ex:" + ex);
        yield break;
      }

      while (true)
      {
        if (operation == null || operation.isDone)
        {
          break;
        }
        else
        {
          info.Progress = operation.progress;
          yield return 1;
        }
      }

      if (operation == null || operation.asset == null)
      {
        UnityEngine.Debug.LogErrorFormat("LoadAssetImplAsync failed bundle:" + para.bundle.name + " resName:" + resName);
        ResUpdateControler.s_UpdateError = ResUpdateError.Create_Assetbundle_Error;
        callback(null);
        yield break;
      }

      s_cacheResObjList.Add(resName, new WeakReference(operation.asset));

      info.IsDone = false;
      info.Progress = 1.0f;
      info.Operation = operation;
      callback(operation.asset);

    }

    internal static IEnumerator LoadLevelImplAsync(ResAsyncInfo info, string levelName, AsyncParam para, MyAction<UnityEngine.Object> callback)
    {
      AsyncOperation operation = null;
      try
      {
        operation = SceneManager.LoadSceneAsync(levelName, para.loadMode);
      }
      catch (System.Exception ex)
      {
        callback(null);
        UnityEngine.Debug.LogErrorFormat("LoadLevelImplAsync failed, levelName:" + levelName + " ex:" + ex);
        yield break;
      }

      while (true)
      {
        if (operation == null || operation.isDone)
        {
          break;
        }
        else
        {
          info.Progress = operation.progress;
          yield return 1;
        }
      }

#if ASSET_CTL_SELF
      if (para.loadMode == LoadSceneMode.Single)
      {
        foreach (var level in s_LoadedScenesList)
        {
          DepAssetBundleRefByResName(level);
        }
        s_LoadedScenesList.Clear();
      }
      s_LoadedScenesList.Add(levelName);
#endif
      if (operation == null)
      {
        UnityEngine.Debug.LogErrorFormat("LoadLevelImplAsync failed, levelName:" + levelName);
        ResUpdateControler.s_UpdateError = ResUpdateError.Create_Assetbundle_Error;
        callback(null);
        yield break;
      }

      info.IsDone = false;
      info.Progress = 1.0f;
      info.Operation = operation;
      callback(null);
    }

    internal static IEnumerator LoadAssetBundleImplAsync(ResAsyncInfo info, string abUrl, MyAction<UnityEngine.Object> callback)
    {
      AssetBundleCreateRequest operation = null;
      try
      {
        operation = AssetBundle.LoadFromFileAsync(abUrl);
      }
      catch (System.Exception ex)
      {
        callback(null);
        UnityEngine.Debug.LogErrorFormat("LoadAssetBundle failed abUrl:" + abUrl + " ex:" + ex);
        yield break;
      }

      while (true)
      {
        if (operation == null || operation.isDone)
        {
          break;
        }
        else
        {
          info.Progress = operation.progress;
          yield return 1;
        }
      }

      if (operation == null || operation.assetBundle == null)
      {
        UnityEngine.Debug.LogErrorFormat("LoadAssetBundle null url:" + abUrl);
        ResUpdateControler.s_UpdateError = ResUpdateError.Create_Assetbundle_Error;
        callback(null);
        yield break;
      }

      info.IsDone = false;
      info.Progress = 1.0f;
      info.Operation = operation;
      callback(operation.assetBundle);
    }
    internal static ResAsyncInfo LoadAssetBundleAsync(string abUrl, MyAction<UnityEngine.Object> callback)
    {
      ResAsyncInfo info = new ResAsyncInfo();

      ResAsyncInfo tryGetValue = null;
      if (s_AsyncLoadAssetbundleList.TryGetValue(abUrl, out tryGetValue))
      {
        UnityEngine.Debug.LogErrorFormat("LoadAssetBundleAsync repeated url:" + abUrl);
        ResUpdateControler.s_UpdateError = ResUpdateError.Create_Assetbundle_Error;
        return ResAsyncInfo.EmptyAsyncInfo;
      }
      s_AsyncLoadAssetbundleList.Add(abUrl, info);

      info.CurCoroutine = CoroutineProxy.Instance.StartCoroutine(LoadAssetBundleImplAsync(info, abUrl, (asset) =>
      {
        callback(asset);
        s_AsyncLoadAssetbundleList.Remove(abUrl);
      }));
      return info;
    }

    internal static UnityEngine.Object GetResFromCache(string resName)
    {
      WeakReference weakRef = null;
      s_cacheResObjList.TryGetValue(resName, out weakRef);

      if (weakRef != null)
      {
        return weakRef.Target as UnityEngine.Object;
      }
      else
      {
        s_cacheAddResObjList.TryGetValue(resName, out weakRef);
        if (weakRef != null)
        {
          return weakRef.Target as UnityEngine.Object;
        }
        return null;
      }
    }

    internal static void DepAssetBundleRefByResName(string resName)
    {
#if ASSET_CTL_SELF
      string assetbundleName = GetAssetBundleNameByResName(resName);
      if (!string.IsNullOrEmpty(assetbundleName))
      {
        AssetBundleCacheConfig abCfgCur = null;
        if (s_cacheAssetBundleCfgList.TryGetValue(assetbundleName, out abCfgCur))
        {
          abCfgCur.refNum--;
          if (abCfgCur.refNum <= 0)
          {
            string[] dependency = manifest.GetAllDependencies(assetbundleName);
            if (dependency != null && dependency.Length > 0)
            {
              foreach (string curDependBundle in dependency)
              {
                AssetBundleCacheConfig abCfg = null;
                if (s_cacheAssetBundleCfgList.TryGetValue(curDependBundle, out abCfg))
                {
                  abCfg.refNum--;
                }
                else
                {
                  throw new ArgumentNullException("AssetBundleCacheConfig");
                }
              }
            }
          }
        }
        else
        {
          throw new ArgumentNullException("AssetBundleCacheConfig");
        }
      }
      else
      {
        throw new ArgumentNullException("assetbundleName");
      }
#endif
    }
#endregion
  }
}